##########################################
## Copyright 2012-2013 Ceruti Francesco
##
##
## This file is part of LiSP (Linux Show Player).
##
## LiSP (Linux Show Player) is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## LiSP (Linux Show Player) is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with LiSP (Linux Show Player).  If not, see <http://www.gnu.org/licenses/>.
##########################################

from PyQt4 import QtCore, QtGui
from lisp.core.plugin import Plugin
from lisp.core.timing import QMediaTime
from lisp.core.media import Media
from lisp.layout.qmediawidget import QMediaWidget
from lisp.plugin.programming import parser


class Programming(Plugin):

    PLUGIN_NAME = "Programming"

    def __init__(self, main):
        self.collector = main.collector
        self.programs = []
        self.media = []

        self.actionProgramming = QtGui.QAction(None, triggered=self.showUI)
        self.actionProgramming.setText("Event programming")
        QMediaWidget.CONTEXT_MENU_ACTIONS.append(QMediaWidget.CONTEXT_MENU_SEPARATOR)
        QMediaWidget.CONTEXT_MENU_ACTIONS.append(self.actionProgramming)

    def showUI(self):
        if(QMediaWidget.CONTEXT_TEMP_MEDIA.conf == Media.NONE):
            QtGui.QMessageBox.critical(None, 'Error Message', 'Empty Media!!')
            return
        pUI = ProgrammingUI(QMediaWidget.CONTEXT_TEMP_MEDIA)
        if('programming' in QMediaWidget.CONTEXT_TEMP_MEDIA.conf):
            pUI.load(QMediaWidget.CONTEXT_TEMP_MEDIA.conf['programming'])
        pUI.exec_()
        if(pUI.result() == pUI.Accepted):
            QMediaWidget.CONTEXT_TEMP_MEDIA.conf['programming'] = pUI.program
            if(QMediaWidget.CONTEXT_TEMP_MEDIA in self.media):
                self.programs[self.media.index(QMediaWidget.CONTEXT_TEMP_MEDIA)].compile(pUI.program)
            elif(pUI.program != ''):
                p = Program(pUI.program, QMediaWidget.CONTEXT_TEMP_MEDIA)
                self.programs.append(p)
                self.media.append(QMediaWidget.CONTEXT_TEMP_MEDIA)

    def reload(self):
        self.reset()

        for media in self.collector.media:
            if(media.state != Media.NONE):
                if('programming' in media.conf):
                    if(media in self.media):
                        self.programs[media].compile(media.conf['programming'])
                    else:
                        p = Program(media.conf['programming'], media)
                        self.programs.append(p)

    def reset(self):
        self.media = []
        self.programs = []


class ProgrammingUI(QtGui.QDialog):

    def __init__(self, media):
        super(ProgrammingUI, self).__init__()

        self.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setMaximumSize(470, 320)
        self.setMinimumSize(470, 320)
        self.resize(470, 320)

        self.media = media
        self.program = ''
        self.actionsStr = ['seek', 'stop', 'pause', 'volume', 'speed']

        self.table = QtGui.QTableWidget(self)
        self.table.setGeometry(QtCore.QRect(5, 5, 350, 310))
        self.table.setColumnCount(3)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.horizontalHeader().hide()

        self.addButton = QtGui.QPushButton(self)
        self.addButton.setGeometry(360, 10, 100, 30)
        self.addButton.setText("Add")

        self.insertButton = QtGui.QPushButton(self)
        self.insertButton.setGeometry(360, 50, 100, 30)
        self.insertButton.setText("Insert")

        self.removeButton = QtGui.QPushButton(self)
        self.removeButton.setGeometry(360, 90, 100, 30)
        self.removeButton.setText("Remove")

        self.acceptButton = QtGui.QPushButton(self)
        self.acceptButton.setGeometry(360, 140, 100, 30)
        self.acceptButton.setText("Ok")

        self.rejectButton = QtGui.QPushButton(self)
        self.rejectButton.setGeometry(360, 180, 100, 30)
        self.rejectButton.setText("Cancel")

        self.current = QtGui.QLabel(self)
        self.current.setGeometry(360, 230, 100, 30)
        self.current.setAlignment(QtCore.Qt.AlignCenter)
        self.current.setText('<b>Selected: </b>0')

        self.addButton.clicked.connect(self.add)
        self.insertButton.clicked.connect(lambda *a: self.insert())
        self.removeButton.clicked.connect(self.remove)
        self.rejectButton.clicked.connect(self.reject)
        self.acceptButton.clicked.connect(self.accept)
        self.table.currentCellChanged.connect(self.currentChanged)

    def insert(self, pos=-1):
        if(pos < 0):
            pos = self.table.currentRow()

        self.table.insertRow(pos)

        actions = QtGui.QComboBox()
        actions.addItems(self.actionsStr)
        actions.currentIndexChanged.connect(lambda i: self.actionChanged(actions))

        self.table.setCellWidget(pos, 0, self.getTimeEditor())
        self.table.setCellWidget(pos, 1, actions)
        self.table.setCellWidget(pos, 2, self.getTimeEditor())

        self.currentChanged()

    def add(self):
        self.insert(self.table.rowCount())

    def currentChanged(self):
        self.current.setText('<b>Selected: </b>' + str(self.table.currentRow() + 1))

    def actionChanged(self, widget):
        row = 0
        while not (self.table.cellWidget(row, 1) is widget) and row < self.table.rowCount():
            row += 1
        action = self.table.cellWidget(row, 1).currentText()
        if(action == 'seek'):
            editor = self.getTimeEditor()
        elif(action == 'volume'):
            editor = QtGui.QDoubleSpinBox()
            editor.setMinimum(0)
            editor.setMaximum(1)
            editor.setValue(self.media.conf['volume']['volume'])
            editor.setSingleStep(0.1)
        elif(action == 'speed'):
            editor = QtGui.QDoubleSpinBox()
            editor.setMinimum(0.1)
            editor.setMaximum(10)
            editor.setValue(self.media.conf['pitch']['tempo'])
            editor.setSingleStep(0.1)
        else:
            editor = QtGui.QWidget()
            editor.setDisabled(True)

        self.table.setCellWidget(row, 2, editor)

    def remove(self):
        self.table.removeRow(self.table.currentRow())

    def accept(self):
        for i in range(self.table.rowCount()):
            time = self.table.cellWidget(i, 0).time()
            time = str(time.minute() * 60 + time.second())
            action = self.table.cellWidget(i, 1).currentText()
            param = self.table.cellWidget(i, 2)
            if(action == 'seek'):
                param = param.time()
                param = ' ' + str(param.minute() * 60 + param.second())
            elif(action == 'volume' or action == 'speed'):
                param = ' ' + str(param.value())
            else:
                param = ''
            self.program += time + ': ' + action + '' + param + ';'
        if(len(self.program) > 0):
            self.program = self.program[:-1]
        super().accept()

    def load(self, program):
        program = program.split(';')
        for p in program:
            self.table.insertRow(self.table.rowCount())
            p = p.split(': ')
            time = int(p[0])
            timeEditor = self.getTimeEditor()
            timeEditor.setTime(QtCore.QTime(0, (time / 60) % 60, time % 60))
            self.table.setCellWidget(self.table.rowCount() - 1, 0, timeEditor)

            p = p[1].split(' ')
            actions = QtGui.QComboBox()
            actions.addItems(self.actionsStr)
            actions.setCurrentIndex(self.actionsStr.index(p[0]))
            actions.currentIndexChanged.connect(lambda index: self.actionChanged(index, actions))
            self.table.setCellWidget(self.table.rowCount() - 1, 1, actions)

            self.actionChanged(actions)
            if(p[0] == 'seek'):
                self.table.cellWidget(self.table.rowCount() - 1, 2).setTime(QtCore.QTime(0, (int(p[1]) / 60) % 60, int(p[1]) % 60))
            elif(p[0] == 'volume' or p[0] == 'speed'):
                self.table.cellWidget(self.table.rowCount() - 1, 2).setValue(float(p[1]))

    def getTimeEditor(self):
        timeEdit = QtGui.QTimeEdit()
        timeEdit.setDisplayFormat('mm.ss')
        timeEdit.setMinimumTime(QtCore.QTime(0, 0, 0))
        timeEdit.setMaximumTime(QtCore.QTime(0, (self.media.conf['duration'] / 60000) % 60, (self.media.conf['duration'] / 1000) % 60))
        return timeEdit


class Program:

    def __init__(self, program, media):
        self.media = media
        self.times = []
        self.events = []

        self.compile(program)

        self.time = QMediaTime(media, wait=0.25)
        self.index = 0

        self.time.progress.connect(self.onProgress, QtCore.Qt.QueuedConnection)
        self.media.onStopped.connect(lambda m: self.reset())
        self.media.onEndOfStream.connect(lambda m: self.reset())
        self.media.onError.connect(lambda m: self.reset())

    def compile(self, program):
        self.times, self.events = parser.parse(program, self.media)

    def onProgress(self, current):
        if(self.index < len(self.times)):
            if(current >= self.times[self.index]):
                self._exec()

    def _exec(self):
        self.events[self.index](self.media)
        self.index += 1
        if(self.index != len(self.times)):
            if(self.times[self.index - 1] == self.times[self.index]):
                self._exec()

    def reset(self):
        self.index = 0
